﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;

using MASAS.MSM.Common.Symbology;
using MASAS.MSM.Common.Utils.ContentType;
using MASAS.MSM.DataLayer.DataMapper;
using MASAS.MSM.DomainLayer;
using MASAS.MSM.DomainLayer.Feed;
using MASAS.MSM.DomainLayer.Logging;
using MASAS.MSM.DomainLayer.Model;
using MASAS.MSM.Services;

namespace MASAS.MSM
{

    /// <summary>
    /// MSM Service class.
    /// </summary>
    public sealed class MasasService
    {

        // Private Static members...
        private static volatile MasasService    _instance   = null;
        private static          object          _syncRoot   = new Object();
        private static          Timer           _schedTimer = null;

        // Private members...
        private HubFacade           _hubFacade      = null;
        private IDataStore          _dataStore      = null;
        private ServiceHost         _pubServiceHost = null;
        private FeedPoller          _feedPoller     = null;
        private MessageHandler      _messageHandler = null;
        private SymbolManager       _symbolMgr      = null;
        private ContentTypeManager  _ctMgr          = null;
        
        // Private read only members...
        private readonly string _csSharePointServerAssemblyName = "msm.SharePoint.Server";
        private readonly string _csSharePointClientAssemblyName = "msm.SharePoint.Client";
        private readonly string _csSharePointRESTAssemblyName = "msm.SharePoint.REST";

        // Public members...
        public enum DataStoreEnum {
            SharePointServer,
            SharePointClient,
            SharePointREST
        }

        /// <summary>
        /// Gets the instance.
        /// </summary>
        public static MasasService Instance {
            get
            {
                if( _instance == null )
                {
                    lock( _syncRoot )
                    {
                        if( _instance == null ) {
                            _instance = new MasasService();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Gets the Symbol Manager.
        /// </summary>
        public SymbolManager SymbolManager { 
            get { return _symbolMgr; }
        }

        /// <summary>
        /// Gets the Content Type Manager.
        /// </summary>
        public ContentTypeManager ContentTypeManager {
            get { return _ctMgr; }
        }

        /// <summary>
        /// Gets or sets the data store.
        /// </summary>
        /// <value>The data store.</value>
        public DataStoreEnum DataStoreType { get; set; }

        /// <summary>
        /// Starts this service.
        /// </summary>
        public void Start()
        {
            InitializeServices();
        }

        /// <summary>
        /// Stops this service.
        /// </summary>
        public void Stop()
        {
            ShutdownServices();
        }
        
        /// <summary>
        /// Prevents a default instance of the <see cref="MasasService"/> class from being created.
        /// </summary>
        private MasasService()
        {
            // Set a default data store...
            DataStoreType = DataStoreEnum.SharePointServer;
        }

        /// <summary>
        /// Initializes the WCF services.
        /// </summary>
        private void InitializeServices()
        {
            Logger.AddLogEntry( "Initializing MASAS Services." );
         
            // Create the Symbol Manager...
            _symbolMgr = new SymbolManager();

            // Create the Content Type Manager...
            _ctMgr = new ContentTypeManager();
   
            // Create the MASAS hub facade...
            _hubFacade = new HubFacade();

            // Load and create the data store...
            _dataStore = LoadDataStore();

            // Create the feed poller...
            _feedPoller = new FeedPoller( _hubFacade );

            // Create a the message handler...
            _messageHandler = new MessageHandler( _feedPoller, _dataStore );

            // Start the Publication service...
            if( _pubServiceHost == null )
            {
                _pubServiceHost = new ServiceHost( typeof( PublicationService ) );
                _pubServiceHost.Open();
            }

            _schedTimer = new Timer( new TimerCallback( TimerEvent ) );
            _schedTimer.Change( 1000, ( 60000 ) );
        }

        /// <summary>
        /// Loads the data store based on the configured type.
        /// </summary>
        /// <returns>IDataStore.</returns>
        private IDataStore LoadDataStore()
        {
            IDataStore dataStore = null;

            string assemblyLocation = AppDomain.CurrentDomain.BaseDirectory;

            switch( DataStoreType )
            {
                case DataStoreEnum.SharePointServer:
                    assemblyLocation += _csSharePointServerAssemblyName;
                    break;
                case DataStoreEnum.SharePointClient:
                    assemblyLocation += _csSharePointClientAssemblyName;
                    break;
                case DataStoreEnum.SharePointREST:
                    assemblyLocation += _csSharePointRESTAssemblyName;
                    break;
                default:
                    break;
            }
            assemblyLocation += ".dll";

            if( File.Exists( assemblyLocation ) )
            {
                var dll = Assembly.LoadFile( assemblyLocation );

                foreach( Type type in dll.GetExportedTypes() )
                {

                    if( typeof( IDataStore ).IsAssignableFrom( type ) )
                    {
                        dataStore = Activator.CreateInstance( type ) as IDataStore;
                        if( dataStore != null )
                        {
                            break;
                        }
                    }
                }
            }

            return dataStore;
        }

        private void ShutdownServices()
        {
            Logger.AddLogEntry( "Stopping MASAS Services." );

            // Let's cleanup he main thread...
            if( _schedTimer != null )
            {
                // Turn off the timer...
                _schedTimer.Change( Timeout.Infinite, Timeout.Infinite );

                // Dispose of the timer, but we may need to wait for it to finish it's task...
                ManualResetEvent disposedEvent = new ManualResetEvent( false );
                _schedTimer.Dispose( disposedEvent );
                disposedEvent.WaitOne();

                // Timer disposed...
                _schedTimer = null;
            }

            // Closing the publication service...
            if( _pubServiceHost != null )
            {
                _pubServiceHost.Close();
                _pubServiceHost = null;
            }

            // Closing the message handler...
            if( _messageHandler != null )
            {
                _messageHandler.Dispose();
                _messageHandler = null;
            }

            // Closing the feed poller...
            if( _feedPoller != null )
            {
                _feedPoller = null;
            }

            // Closing the data store...
            if( _dataStore != null )
            {
                _dataStore.Dispose();
                _dataStore = null;
            }

            // Closing the MASAS hub facade...
            if( _hubFacade != null )
            {
                _hubFacade = null;
            }

            // Cleanup the Content Type Manager...
            if( _ctMgr != null )
            {
                _ctMgr = null;
            }

            // Cleanup the Symbol Manager...
            if( _symbolMgr != null )
            {
                _symbolMgr = null;
            }
        }

        /// <summary>
        /// Event timer.
        /// </summary>
        /// <param name="stateInfo">The state info.</param>
        static private void TimerEvent( Object stateInfo )
        {
            try
            {
                // Stop the timer...
                _schedTimer.Change( Timeout.Infinite, Timeout.Infinite );

                Logger.AddLogEntry( "Fetching feed data." );
                List<Hub> hubs = _instance._dataStore.GetRegisteredHubs();

                foreach( Hub hub in hubs )
                {
                    string hubDetails = string.Format( "Hub {0}: URL({1}), Access({2})", hub.Name, hub.URI, hub.Access );
                    Logger.AddLogEntry( hubDetails );

                    foreach( Filter filter in hub.Filters )
                    {
                        string filterDetails = string.Format( "Filter {0}: Enabled({1}), Value({2}), Priority({3})", filter.Name, filter.Enabled, filter.Value, filter.Priority );
                        Logger.AddLogEntry( filterDetails );
                    }
                }

                _instance._feedPoller.CheckFeed( hubs );

            }
            catch( Exception ex )
            {
                // Generic catch all!
                Logger.AddLogEntry( "An unhandled exception has occured!", ex );
            }

            // Restart the timer...
            try
            {
                _schedTimer.Change( 60000, ( 60000 ) );
            }
            catch( ObjectDisposedException ex )
            {
                Logger.AddLogEntry( "An ObjectDisposedException has occured!", ex );
            }
            catch( Exception ex )
            {
                Logger.AddLogEntry( "An unknown exception has occured!", ex );
            }
        }

    }

}